//**************************************************
// Description: get addr and data from async FIFO and access NP registers
//**************************************************

//**************************************************
// include files
//**************************************************
`include "protocol_define.v"

module AHB_deal #(
    parameter ORDER_WIDTH           = 30    ,
    parameter ADDR_WIDTH            = 32    ,
    parameter DATA_WIDTH            = 32 
    ) (
    input                                   clk                 ,
    input                                   rst_n               ,
    // pre-module(async FIFO)
    output                                  rx_rd_en            ,
    input       [ORDER_WIDTH-1:0]           rx_rd_order         ,
    input                                   rx_rd_frame_start   ,
    input                                   rx_rd_action_wr     ,
    input       [ADDR_WIDTH-1:0]            rx_rd_addr          ,
    input       [DATA_WIDTH-1:0]            rx_rd_data          ,
    input                                   rx_rd_empty         , // sync with "rd_data"
    // post-module(NP AHB interface) [rx channel] 
    // note: all control signal is in the 1st cycle !
    output      [ADDR_WIDTH-1:0]            AHB_haddr_m         ,
    output      [DATA_WIDTH-1:0]            AHB_hwdata_m        ,
    output                                  AHB_hwrite_m        ,
    output      [2:0]                       AHB_hsize_m         , // can hold "3'b010" (32-bit data bus)
    output      [2:0]                       AHB_hburst_m        , // can hold "3'b000" (single burst)
    output      [1:0]                       AHB_htrans_m        , // "2'b10" means "NONSEQ", "2'b00" means "IDLE"
    output      [3:0]                       AHB_hmaster_m       , // can hold "4'b0000"(Exclusive Transfer master identifier) [AHB5]
    output                                  AHB_hnonsec_m       , // can hold "1'b0"(non-Secure Transfer) [AHB5]
    output                                  AHB_hexcl_m         , // can hold "1'b0"(non-Exclusive Transfer) [AHB5]
    output                                  AHB_hmasterlock_m   , // can hold "1'b0" or keep "1" during transfer(must has a "IDLE" in HTRANS) [do not require]
    output      [6:0]                       AHB_hprot_m         , // can hold "7'b00_0001"(data access)
    input       [DATA_WIDTH-1:0]            AHB_hrdata_m        ,
    input                                   AHB_hready_m        , // "1'b1" means need another cycle
    input                                   AHB_hresp_m         , // "1'b1" means transfer is ERROR(ERROR needs two cycle: 1st hreadyout=0, 2nd hreadyout=1(HTRANS must be "IDLE" at the same time))
    input                                   AHB_hexokay_m       , // can hold "1'b1"(Exclusive transfer success) [AHB5]
    // tx channel
    output                                  tx_wr_en            ,
    output      [ORDER_WIDTH-1:0]           tx_wr_order         ,
    output                                  tx_wr_frame_start   ,
    output                                  tx_wr_action_wr     ,
    output      [ADDR_WIDTH-1:0]            tx_wr_addr          ,
    output      [DATA_WIDTH-1:0]            tx_wr_data          ,
    input                                   tx_wr_full          
) ;
    //----------------------------------------------
    // async FIFO empty signal delay 2 cycle (note)
    //----------------------------------------------
    reg     rx_rd_empty_ff1 ;
    reg     rx_rd_empty_ff2 ;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            rx_rd_empty_ff1 <= 1'b1 ;
            rx_rd_empty_ff2 <= 1'b1 ;
        end
        else begin
            rx_rd_empty_ff1 <= rx_rd_empty ;
            rx_rd_empty_ff2 <= rx_rd_empty_ff1 ;
        end
    end

//**************************************************
// output signal assign
//**************************************************
    assign AHB_hsize_m              = 3'b010        ; //        32-bit data
    assign AHB_hburst_m             = 3'b000        ; //        single burst
    assign AHB_hmaster_m            = 4'b0000       ; // [AHB5] Exclusive Transfer master identifier 
    assign AHB_hnonsec_m            = 1'b0          ; // [AHB5] non-Secure Transfer
    assign AHB_hexcl_m              = 1'b0          ; // [AHB5] non-exclusive Transfer
    assign AHB_hmasterlock_m        = 1'b0          ; //        not lock master
    assign AHB_hprot_m              = 7'b000_0001   ; //        data access

    localparam HTRANS_STATE_IDLE    = 2'b00         ;
    localparam HTRANS_STATE_NONSEQ  = 2'b10         ;

    //----------------------------------------------
    // FIFO read signal declare
    //----------------------------------------------
    reg                             rx_rd_en_r ;
    //----------------------------------------------
    // AHB output data register declare
    //----------------------------------------------
    reg     [ADDR_WIDTH-1:0]        AHB_haddr_m_r   ;
    reg     [DATA_WIDTH-1:0]        AHB_hwdata_m_r  ;
    reg                             AHB_hwrite_m_r  ;
    reg     [1:0]                   AHB_htrans_m_r  ;
    reg                             AHB_hresp_m_r   ;
    // write data 
    reg     [DATA_WIDTH-1:0]        rx_rd_data_ff   ;
    // read data
    reg     [DATA_WIDTH-1:0]        AHB_hrdata_m_ff ;


//**************************************************
// FSM
//**************************************************
    localparam AHB_IDLE             = 3'd0             ;
    localparam AHB_WAIT             = 3'd1             ;
    localparam AHB_CTRL             = 3'd2             ;
    localparam AHB_WR_DATA          = 3'd3             ;
    localparam AHB_RD_DATA          = 3'd4             ;
    localparam AHB_WR_WAIT_RESP     = 3'd5             ;
    localparam AHB_RD_WAIT_RESP     = 3'd6             ;
    localparam AHB_END              = 3'd7             ;

    reg     [2:0]                   AHB_deal_state_c ;
    reg     [2:0]                   AHB_deal_state_n ;

    // FSM(1)
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            AHB_deal_state_c <= AHB_IDLE ;
        else
            AHB_deal_state_c <= AHB_deal_state_n ;
    end
    // FSM(2)
    always @(*) begin
        case (AHB_deal_state_c)
            AHB_IDLE: begin
                if(rx_rd_empty_ff2==1'b0)
                    AHB_deal_state_n = AHB_WAIT ;
                else
                    AHB_deal_state_n = AHB_IDLE ;
            end

            AHB_WAIT: begin
                AHB_deal_state_n = AHB_CTRL ;
            end

            AHB_CTRL: begin
                if(AHB_hready_m==1'b1) begin
                    if(rx_rd_action_wr==1'b1)
                        AHB_deal_state_n = AHB_WR_DATA ;
                    else
                        AHB_deal_state_n = AHB_RD_DATA ;
                end
                else 
                    AHB_deal_state_n = AHB_CTRL ;
            end

            AHB_WR_DATA: begin
                AHB_deal_state_n = AHB_WR_WAIT_RESP ;
            end

            AHB_RD_DATA: begin
                AHB_deal_state_n = AHB_RD_WAIT_RESP ;
            end
            
            AHB_WR_WAIT_RESP: begin
                if(AHB_hready_m==1'b1)
                    AHB_deal_state_n = AHB_END ;
                else
                    AHB_deal_state_n = AHB_WR_WAIT_RESP ;
            end

            AHB_RD_WAIT_RESP: begin
                if(AHB_hready_m==1'b1)
                    AHB_deal_state_n = AHB_END ;
                else
                    AHB_deal_state_n = AHB_RD_WAIT_RESP ;
            end

            AHB_END: begin
                if(rx_rd_en==1'b1)
                    AHB_deal_state_n = AHB_CTRL ;
                else
                    AHB_deal_state_n = AHB_IDLE ;
            end

            default: begin
                AHB_deal_state_n = AHB_IDLE ;
            end
        endcase
    end
    // FSM(3)
    // AHB:
    // "haddr" and "htrans"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            AHB_haddr_m_r   <= {(ADDR_WIDTH){1'b0}} ;
            AHB_htrans_m_r  <= HTRANS_STATE_IDLE ;
        end
        else if( AHB_deal_state_c == AHB_CTRL ) begin
            AHB_haddr_m_r   <= rx_rd_addr ;
            AHB_htrans_m_r  <= HTRANS_STATE_NONSEQ ;
        end
        else begin
            AHB_haddr_m_r   <= {(ADDR_WIDTH){1'b0}} ;
            AHB_htrans_m_r  <= HTRANS_STATE_IDLE ;
        end
    end
    // "hwdata"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            AHB_hwdata_m_r  <= {(DATA_WIDTH){1'b0}} ;
        else if( AHB_deal_state_c == AHB_WR_DATA )
            AHB_hwdata_m_r  <= rx_rd_data_ff ;
        else
            AHB_hwdata_m_r  <= AHB_hwdata_m_r ;
    end
    // "hwrite"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            AHB_hwrite_m_r  <= 1'b0 ;
        else if( AHB_deal_state_c == AHB_CTRL )
            AHB_hwrite_m_r  <= rx_rd_action_wr ;
        else
            AHB_hwrite_m_r  <= 1'b0 ;
    end
    // "hrdata"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            AHB_hrdata_m_ff <= {(DATA_WIDTH){1'b0}} ;
        else if( (AHB_deal_state_c == AHB_RD_WAIT_RESP) && (AHB_hready_m==1'b1) )
            AHB_hrdata_m_ff <= AHB_hrdata_m ;
        else
            AHB_hrdata_m_ff <= AHB_hrdata_m_ff ;
    end
    // "hresp"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            AHB_hresp_m_r   <= 1'b0 ;
        else if( AHB_hready_m == 1'b1 )
            AHB_hresp_m_r   <= AHB_hresp_m ;
        else
            AHB_hresp_m_r   <= AHB_hresp_m_r ;
    end

    // FIFO read enable :
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            rx_rd_en_r      <= 1'b0 ;
        else if( 
                    (   
                        (AHB_deal_state_c == AHB_IDLE) || 
                            (   ((AHB_deal_state_c == AHB_WR_WAIT_RESP) || (AHB_deal_state_c == AHB_RD_WAIT_RESP)) && 
                                AHB_hready_m    )     
                    ) 
                    && 
                    ( rx_rd_empty_ff2 == 1'b0 ) 
                )
            rx_rd_en_r      <= 1'b1 ;
        else
            rx_rd_en_r      <= 1'b0 ;
    end

    //----------------------------------------------
    // FIFO and AHB write assign
    //----------------------------------------------
    assign rx_rd_en = rx_rd_en_r ;
    assign AHB_haddr_m = AHB_haddr_m_r ;
    assign AHB_hwdata_m = AHB_hwdata_m_r ;
    assign AHB_hwrite_m = AHB_hwrite_m_r ;
    assign AHB_htrans_m = AHB_htrans_m_r ;



    //----------------------------------------------
    // In "AHB_CTRL" state: store the FIFO out data
    //----------------------------------------------
    reg     [ORDER_WIDTH-1:0]           rx_rd_order_ff ;
    reg                                 rx_rd_frame_start_ff ;
    reg                                 rx_rd_action_wr_ff ;
    reg     [ADDR_WIDTH-1:0]            rx_rd_addr_ff ;
    // reg     [DATA_WIDTH-1:0]            rx_rd_data_ff ;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            rx_rd_order_ff       <=  {(ORDER_WIDTH){1'b0}}                          ;
            rx_rd_frame_start_ff <=  1'b0                                           ;
            rx_rd_action_wr_ff   <=  1'b0                                           ;
            rx_rd_addr_ff        <=  {(ADDR_WIDTH){1'b0}}                           ;
            rx_rd_data_ff        <=  {(DATA_WIDTH){1'b0}}                           ;
        end
        else if( AHB_deal_state_c == AHB_CTRL ) begin
            rx_rd_order_ff       <=  rx_rd_order                                    ;
            rx_rd_frame_start_ff <=  rx_rd_frame_start                              ;
            rx_rd_action_wr_ff   <=  rx_rd_action_wr                                ;
            rx_rd_addr_ff        <=  rx_rd_addr                                     ;
            rx_rd_data_ff        <=  rx_rd_data                                     ;
        end
        else begin
            rx_rd_order_ff       <=  rx_rd_order_ff                                 ;
            rx_rd_frame_start_ff <=  rx_rd_frame_start_ff                           ;
            rx_rd_action_wr_ff   <=  rx_rd_action_wr_ff                             ;
            rx_rd_addr_ff        <=  rx_rd_addr_ff                                  ;
            rx_rd_data_ff        <=  rx_rd_data_ff                                  ;
        end
    end



    //----------------------------------------------
    // tx channel FIFO data write
    //----------------------------------------------
    reg                             tx_wr_en_r              ;
    reg     [ORDER_WIDTH-1:0]       tx_wr_order_r           ;
    reg                             tx_wr_frame_start_r     ;
    reg                             tx_wr_action_wr_r       ;
    reg     [ADDR_WIDTH-1:0]        tx_wr_addr_r            ;
    reg     [DATA_WIDTH-1:0]        tx_wr_data_r            ;

    always @(posedge clk or negedge rst_n) begin
        if( !rst_n ) begin
            tx_wr_en_r              <= 1'b0                                                 ;
            tx_wr_order_r           <= {(ORDER_WIDTH){1'b0}}                                ;
            tx_wr_frame_start_r     <= 1'b0                                                 ;
            tx_wr_action_wr_r       <= 1'b0                                                 ;
            tx_wr_addr_r            <= {(ADDR_WIDTH){1'b0}}                                 ;
            tx_wr_data_r            <= {(DATA_WIDTH){1'b0}}                                 ;
        end
        else if( AHB_deal_state_c == AHB_END ) begin
            tx_wr_en_r              <= 1'b1 ;
            tx_wr_order_r           <= {rx_rd_order_ff[ORDER_WIDTH-1:1], AHB_hresp_m_r}     ;
            tx_wr_frame_start_r     <= rx_rd_frame_start_ff                                 ;
            tx_wr_action_wr_r       <= rx_rd_action_wr_ff                                   ;
            tx_wr_addr_r            <= rx_rd_addr_ff                                        ;
            tx_wr_data_r            <= AHB_hrdata_m_ff                                      ;
        end
        else begin
            tx_wr_en_r              <= 1'b0                                                 ;
            tx_wr_order_r           <= {(ORDER_WIDTH){1'b0}}                                ;
            tx_wr_frame_start_r     <= 1'b0                                                 ;
            tx_wr_action_wr_r       <= 1'b0                                                 ;
            tx_wr_addr_r            <= {(ADDR_WIDTH){1'b0}}                                 ;
            tx_wr_data_r            <= {(DATA_WIDTH){1'b0}}                                 ;
        end
    end

    // FIFO write assign
    assign tx_wr_en             =   tx_wr_en_r              ;
    assign tx_wr_order          =   tx_wr_order_r           ;
    assign tx_wr_frame_start    =   tx_wr_frame_start_r     ;
    assign tx_wr_action_wr      =   tx_wr_action_wr_r       ;
    assign tx_wr_addr           =   tx_wr_addr_r            ;
    assign tx_wr_data           =   tx_wr_data_r            ;

endmodule
